﻿using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;

namespace DotNetCommon;

/// <summary>
/// json 节点类型
/// </summary>
public enum JsonTreeNodeType
{
    Object,
    Array,
    String,
    Number,
    Boolean,
    Null
}

/// <summary>
/// 用于快速访问并修改 JSON 的树形结构
/// </summary>
public class JsonTreeNode
{
    /// <summary>
    /// json节点名称, 可能是属性名或数组索引
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// json节点路径, 例如 "$.\"property\"[0].\"subProperty\""
    /// </summary>
    public string Path { get; set; }
    /// <summary>
    /// json节点类型
    /// </summary>
    public JsonTreeNodeType Type { get; set; }
    /// <summary>
    /// json节点值, 对于对象和数组类型为 null
    /// </summary>
    public object Value { get; set; }
    /// <summary>
    /// json节点的子节点列表, 对于非对象和非数组类型为 null
    /// </summary>
    public List<JsonTreeNode> Children { get; set; }
    private JsonTreeNode(string name, JsonTreeNodeType type, string path, object value = null)
    {
        Name = name;
        Path = path;
        Type = type;
        Value = value;
    }
    private void AddChild(JsonTreeNode child)
    {
        Children ??= [];
        Children.Add(child);
    }

    /// <summary>
    /// 解析 JSON 字符串为 JsonTreeNode 树形结构
    /// </summary>
    /// <remarks>
    /// 注意: 采用 System.Text.Json 解析，忽略注释和尾随逗号。
    /// </remarks>
    public static JsonTreeNode Parse(string json)
    {
        using var document = JsonDocument.Parse(json, new JsonDocumentOptions
        {
            AllowTrailingCommas = true,
            CommentHandling = JsonCommentHandling.Skip
        });
        return ParseElement(document.RootElement);
    }

    /// <summary>
    /// 访问树形结构中的每个节点
    /// </summary>
    public void Visit(Action<VisitTreeContext<JsonTreeNode>> act)
    {
        if (Children == null || Children.Count == 0) return;
        Children.VisitTree(i => i.Children, act);
    }

    /// <summary>
    /// 根据现有的内容, 重新生成一个 JsonNode
    /// </summary>
    public JsonNode BuildJsonNode()
    {
        switch (Type)
        {
            case JsonTreeNodeType.Object:
                var obj = new JsonObject();
                if (Children != null)
                {
                    foreach (var child in Children)
                    {
                        obj[child.Name] = child.BuildJsonNode();
                    }
                }
                return obj;

            case JsonTreeNodeType.Array:
                var arr = new JsonArray();
                if (Children != null)
                {
                    foreach (var child in Children)
                    {
                        arr.Add(child.BuildJsonNode());
                    }
                }
                return arr;

            case JsonTreeNodeType.String:
                return JsonValue.Create(Value?.ToString());

            case JsonTreeNodeType.Number:
                if (Value is int i) return JsonValue.Create(i);
                if (Value is decimal d) return JsonValue.Create(d);
                if (Value is long l) return JsonValue.Create(l);
                if (Value is double dou) return JsonValue.Create(dou);
                return JsonValue.Create((decimal)Value);

            case JsonTreeNodeType.Boolean:
                return JsonValue.Create((bool)Value);

            case JsonTreeNodeType.Null:
                return null;

            default:
                return JsonValue.Create(Value?.ToString());
        }
    }

    /// <summary>
    /// 根据现有的内容, 重新生成一个 JsonNode 并调用 JsonNode.ToJsonString
    /// </summary>
    public string ToJsonString(string dateTimeFormatString = null,
        string dateTimeOffsetFormatString = null,
        string dateOnlyFormatString = null,
        string timeOnlyFormatString = null,
        bool ignoreNull = false,
        bool enum2String = false,
        bool lowerCamelCase = false,
        bool lowerCamelCaseDictionaryKey = false,
        bool isIntend = false,
        bool number2String = false,
        bool ignoreJsonPropertyName = false,
        Action<JsonSerializerOptions> otherSettings = null)
    {
        var node = BuildJsonNode();
        if (node == null)
            return "null";
        return node.ToJsonFast(dateTimeFormatString: dateTimeFormatString,
            dateTimeOffsetFormatString: dateTimeOffsetFormatString,
            dateOnlyFormatString: dateOnlyFormatString,
            timeOnlyFormatString: timeOnlyFormatString,
            ignoreNull: ignoreNull,
            enum2String: enum2String,
            lowerCamelCase: lowerCamelCase,
            lowerCamelCaseDictionaryKey: lowerCamelCaseDictionaryKey,
            isIntend: isIntend,
            number2String: number2String,
            ignoreJsonPropertyName: ignoreJsonPropertyName,
            otherSettings: otherSettings
            );
    }

    /// <summary>
    /// 异步访问树形结构中的每个节点
    /// </summary>
    public async Task Visit(Func<VisitTreeContext<JsonTreeNode>, Task> act)
    {
        if (Children == null || Children.Count == 0) return;
        await Children.VisitTree(i => i.Children, act);
    }

    private static string ProtectPropertyName(string prop)
    {
        return "\"" + prop.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n") + "\"";
    }
    private static JsonTreeNode ParseElement(JsonElement element, string name = null, string path = "$")
    {
        switch (element.ValueKind)
        {
            case JsonValueKind.Object:
                var objNode = new JsonTreeNode(name, JsonTreeNodeType.Object, path);
                foreach (var property in element.EnumerateObject())
                {
                    objNode.AddChild(ParseElement(property.Value, property.Name, $"{path}.{ProtectPropertyName(property.Name)}"));
                }
                return objNode;

            case JsonValueKind.Array:
                var arrayNode = new JsonTreeNode(name, JsonTreeNodeType.Array, path);
                int index = 0;
                foreach (var item in element.EnumerateArray())
                {
                    arrayNode.AddChild(ParseElement(item, index.ToString(), $"{path}[{index}]"));
                    index++;
                }
                return arrayNode;

            case JsonValueKind.String:
                return new JsonTreeNode(name, JsonTreeNodeType.String, path, element.GetString());

            case JsonValueKind.Number:
                // 尝试获取为 decimal，如果失败则获取为 string
                if (element.TryGetDecimal(out decimal decimalValue))
                    return new JsonTreeNode(name, JsonTreeNodeType.Number, path, decimalValue);
                else
                    return new JsonTreeNode(name, JsonTreeNodeType.Number, path, element.GetDouble());

            case JsonValueKind.True:
            case JsonValueKind.False:
                return new JsonTreeNode(name, JsonTreeNodeType.Boolean, path, element.GetBoolean());

            case JsonValueKind.Null:
                return new JsonTreeNode(name, JsonTreeNodeType.Null, path);

            default:
                return new JsonTreeNode(name, JsonTreeNodeType.String, path, element.ToString());
        }
    }
}
